package com.os.service;

import cn.hutool.json.JSONUtil;
import com.os.model.Product;
import com.os.repository.ProductRepository;
import com.os.repository.ProductSearchRequestBuild;
import org.opensearch.action.bulk.BulkRequest;
import org.opensearch.action.bulk.BulkResponse;
import org.opensearch.action.index.IndexRequest;
import org.opensearch.action.search.*;
import org.opensearch.client.RequestOptions;
import org.opensearch.client.RestHighLevelClient;
import org.opensearch.client.core.CountRequest;
import org.opensearch.common.StopWatch;
import org.opensearch.common.unit.TimeValue;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.search.Scroll;
import org.opensearch.search.SearchHit;
import org.opensearch.search.SearchHits;
import org.opensearch.search.builder.SearchSourceBuilder;
import org.opensearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Service
public class ProductService {

    @Resource
    RestHighLevelClient client;

    @Resource
    ProductRepository productRepository;

    public void saveProduct() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();

        for (int i = 1; i < 11000; i++) {
            Product product = new Product(i, "name" + i, new BigDecimal(i));
            // String productStr = JSONUtil.toJsonStr(product);
            IndexRequest indexRequest = new IndexRequest("products")
                    .id(product.getId().toString())  // 设置 ID（可选，默认会自动生成）
                    .source("id", i, "name", product.getName(), "price", product.getPrice());
                    // .source(productStr);
            bulkRequest.add(indexRequest);
        }

        // 执行批量插入操作
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);

        // 如果有失败的请求，可以在这里处理
        if (bulkResponse.hasFailures()) {
            System.out.println("批量插入失败！");
            System.out.println(bulkResponse.buildFailureMessage());
        } else {
            System.out.println("批量插入成功！");
        }
    }

    public List<Product> get(String name) throws IOException {
        // 创建查询请求，指定索引名
        SearchRequest searchRequest = new SearchRequest("products");

        // 构建查询条件，这里我们使用 match 查询
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery("name", name));  // 查询 name 字段为 "Laptop" 的文档

        searchRequest.source(sourceBuilder);

        // 执行查询
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        // 获取查询结果
        SearchHits hits = searchResponse.getHits();

        // 打印查询结果
        List<Product> productList = new ArrayList<>();
        hits.forEach(item -> {
            Product product = JSONUtil.toBean(item.getSourceAsString(), Product.class);
            productList.add(product);
        });

        return productList;
    }

    public Integer getAll() throws IOException {
        // 创建一个查询请求，指定索引名
        SearchRequest searchRequest = new SearchRequest("products");

        // 构建查询条件，这里为空，表示查询所有数据
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery()); // 使用 match_all 查询，返回所有数据

        searchRequest.source(sourceBuilder);

        // 执行查询
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        // 获取查询结果
        SearchHits hits = searchResponse.getHits();

        List<Product> productList = new ArrayList<>();
        hits.forEach(item -> {
            Product product = JSONUtil.toBean(item.getSourceAsString(), Product.class);
            productList.add(product);
        });
        return productList.size();
    }

    public List<Product> page(int pageNo, int pageSize) {
        StopWatch stopWatch = new StopWatch();
        // 启动计时器
        stopWatch.start();
        SearchRequest searchRequest = ProductSearchRequestBuild.pageSearchRequest(pageNo, pageSize);
        List<Product> page = productRepository.page(searchRequest, Product.class);
        // 停止计时器
        stopWatch.stop();
        // 输出执行时间（单位：毫秒）
        System.out.println("方法执行时间: " + stopWatch.totalTime() + " 毫秒");
        System.out.println("=====数据量条数:" + page.size());
        return page;
    }

    public Long count() {

        CountRequest countRequest = ProductSearchRequestBuild.countSearchRequest();

        return productRepository.count(countRequest);
    }

    /**
     * 该方法缺点：
     * 1.每次读取的长度就是目标页的长度，会导致多次读取，比较耗时
     * 2.此外判断获取数据时，范围跟pageSize一样
     * @param pageNumber
     * @param pageSize
     * @return
     * @throws Exception
     */
    public List<Product> scroll(int pageNumber, int pageSize) throws Exception {
        StopWatch stopWatch = new StopWatch();
        // 启动计时器
        stopWatch.start();
        // 计算要跳过的记录数，pageNumber * pageSize
        int skipRecords = (pageNumber - 1) * pageSize;

        // 初始化滚动查询
        Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1)); // 设置滚动时间窗口为 1 分钟
        SearchRequest searchRequest = new SearchRequest("products"); // 替换为你的索引名

        // 创建查询条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());  // 查询所有数据
        sourceBuilder.size(pageSize);  // 每页10条记录
        sourceBuilder.sort("id", SortOrder.ASC);  // 使用_doc进行排序优化滚动查询

        searchRequest.source(sourceBuilder);
        searchRequest.scroll(scroll);

        // 执行查询
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();

        // 数据集合
        List<String> resultList = new ArrayList<>();

        // 通过滚动查询读取指定页的数据
        int currentSkip = pageSize;
        while (hits.getHits().length > 0) {
            // 每次获取一批数据，直到跳过所需记录数
            if (currentSkip > skipRecords) {
                // 处理数据
                for (SearchHit hit : hits) {
                    // 将每条记录的源数据转换为字符串，并存入结果集合
                    resultList.add(hit.getSourceAsString());
                }
                // 一旦跳过足够的记录，停止查询
                break;
            }

            // 如果还没有跳过足够的记录，继续滚动
            if (currentSkip <= skipRecords) {
                // 使用scrollId进行下一次滚动查询
                String scrollId = searchResponse.getScrollId();
                searchResponse = client.scroll(new SearchScrollRequest(scrollId).scroll(scroll), RequestOptions.DEFAULT);
                hits = searchResponse.getHits();

                // 更新当前跳过的记录数
                currentSkip += hits.getHits().length;
            }
        }

        // 清理滚动查询
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
        clearScrollRequest.addScrollId(searchResponse.getScrollId());
        client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);

        List<Product> productList = resultList.stream().map(item -> JSONUtil.toBean(item, Product.class)).toList();
        // 停止计时器
        stopWatch.stop();
        // 输出执行时间（单位：毫秒）
        System.out.println("方法执行时间: " + stopWatch.totalTime() + " 毫秒");
        System.out.println("=====数据量条数:" + productList.size());
        // 输出查询结果
        return productList;
    }

    public List<Product> scroll1(Integer pageNo, Integer pageSize) {
        List<Product> search = new ArrayList<>();
        StopWatch stopWatch = new StopWatch();
        // 启动计时器
        stopWatch.start();

        // 先查询目标的数据量
        int targetHitsCount = pageNo * pageSize;
        Long count = productRepository.count(ProductSearchRequestBuild.countSearchRequest());
        if (count <= 10000 || targetHitsCount <= 10000) {
            SearchRequest searchRequest = ProductSearchRequestBuild.pageSearchRequest(pageNo, pageSize);
            search =  productRepository.page(searchRequest, Product.class);
        } else {
            // 可以自定义搜索条件
            SearchRequest searchRequest = ProductSearchRequestBuild.scrollSearchRequest();
            search = productRepository.search(searchRequest, pageNo, pageSize, Product.class);
        }
        // 停止计时器
        stopWatch.stop();
        // 输出执行时间（单位：毫秒）
        System.out.println("方法执行时间: " + stopWatch.totalTime() + " 毫秒");
        System.out.println("=====数据量条数:" + search.size());
        return search; // 返回当前页的产品列表
    }

    public List<Product> export() {
        StopWatch stopWatch = new StopWatch();
        // 启动计时器
        stopWatch.start();
        // 可以自定义搜索条件
        SearchRequest searchRequest = ProductSearchRequestBuild.scrollSearchRequest();
        List<Product> search = productRepository.search(searchRequest, Product.class);
        // 停止计时器
        stopWatch.stop();
        // 输出执行时间（单位：毫秒）
        System.out.println("方法执行时间: " + stopWatch.totalTime() + " 毫秒");
        System.out.println("=====数据量条数:" + search.size());
        return search; // 返回所有产品列表
    }


}